//The MIT License (MIT)
//
//Copyright (c) 2018 dote
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of
//this software and associated documentation files (the "Software"), to deal in
//the Software without restriction, including without limitation the rights to
//use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
//the Software, and to permit persons to whom the Software is furnished to do so,
//subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
//FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
//COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
//IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package com.github.dote.mio.filter.ext;


import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class PageImpl<T> implements Page<T>, Serializable {
    private static final long serialVersionUID = 867755909294344406L;
    private final List<T> content;
    private final Pageable pageable;
    private final long total;

    public PageImpl(List<T> content, Pageable pageable, long total) {
        this.content = new ArrayList();
        if (null == content) {
            throw new IllegalArgumentException("Content must not be null!");
        } else {
            this.content.addAll(content);
            this.total = total;
            this.pageable = pageable;
        }
    }

    public PageImpl(List<T> content) {
        this(content, (Pageable)null, null == content ? 0L : (long)content.size());
    }

    public int getNumber() {
        return this.pageable == null ? 0 : this.pageable.getPageNumber();
    }

    public int getSize() {
        return this.pageable == null ? 0 : this.pageable.getPageSize();
    }

    public int getTotalPages() {
        return this.getSize() == 0 ? 1 : (int)Math.ceil((double)this.total / (double)this.getSize());
    }

    public int getNumberOfElements() {
        return this.content.size();
    }

    public long getTotalElements() {
        return this.total;
    }

    public boolean hasPreviousPage() {
        return this.getNumber() > 0;
    }

    public boolean isFirstPage() {
        return !this.hasPreviousPage();
    }

    public boolean hasNextPage() {
        return this.getNumber() + 1 < this.getTotalPages();
    }

    public boolean isLastPage() {
        return !this.hasNextPage();
    }

    public Pageable nextPageable() {
        return this.hasNextPage() ? this.pageable.next() : null;
    }

    public Pageable previousPageable() {
        return this.hasPreviousPage() ? this.pageable.previousOrFirst() : null;
    }

    public Iterator<T> iterator() {
        return this.content.iterator();
    }

    public List<T> getContent() {
        return Collections.unmodifiableList(this.content);
    }

    public boolean hasContent() {
        return !this.content.isEmpty();
    }

    public Sort getSort() {
        return this.pageable == null ? null : this.pageable.getSort();
    }

    public String toString() {
        String contentType = "UNKNOWN";
        if (this.content.size() > 0) {
            contentType = this.content.get(0).getClass().getName();
        }

        return String.format("Page %s of %d containing %s instances", this.getNumber(), this.getTotalPages(), contentType);
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (!(obj instanceof PageImpl)) {
            return false;
        } else {
            PageImpl<?> that = (PageImpl)obj;
            boolean totalEqual = this.total == that.total;
            boolean contentEqual = this.content.equals(that.content);
            boolean pageableEqual = this.pageable == null ? that.pageable == null : this.pageable.equals(that.pageable);
            return totalEqual && contentEqual && pageableEqual;
        }
    }

    public int hashCode() {
        int result1 = 17;
        int result = 31 * result1 + (int)(this.total ^ this.total >>> 32);
        result = 31 * result + (this.pageable == null ? 0 : this.pageable.hashCode());
        result = 31 * result + this.content.hashCode();
        return result;
    }
}
